package main

import (
	"log"
	"fmt"
	"net/http"
	"image"
	"bytes"
	"io"
	"io/ioutil"
	"strings"
	_ "image/png"
	_ "image/jpeg"
	_ "image/gif"
	"code.google.com/p/gorilla/mux"
	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/s3"
	"code.google.com/p/go-uuid/uuid"
)

const MAX_WIDTH = 600
const MAX_HEIGHT = 800
const BUCKETID = "bottlcap"
var Bucket *s3.Bucket

var extensions = map[string]string{
	"audio/mpeg3" : "mp3",
	"image/jpeg" : "jpg",
	"image/gif" : "gif",
	"image/png" : "png",
	"video/mpeg" : "mp4",
}

func handleStream(w http.ResponseWriter, req *http.Request) {

	defer req.Body.Close()

	contentType := req.Header.Get("Content-Type")

	log.Println("handling media " + contentType)
	
	data, err := ioutil.ReadAll(req.Body)

	if err != nil {
		w.WriteHeader(500)
		log.Println(err)
		return
	}

	id := uuid.New()

	ext := extensions[contentType]

	path := fmt.Sprintf("%s-%s", id, ext)

	log.Println("uploading " + path + " ... ")

	err = Bucket.Put(path, data, contentType, s3.PublicRead)

	if err != nil {
		w.WriteHeader(500)
		log.Println(err)
		return
	}

	urlstr := Bucket.URL(path)

	w.Header().Set("Location", urlstr)
	w.Header().Set("X-Location", urlstr)

	w.WriteHeader(200)

	log.Println("... done ", path)
}

func handleImageHelper(w http.ResponseWriter, req *http.Request, reader io.Reader) {

	data, err := ioutil.ReadAll(reader)
	defer req.Body.Close()

	if err != nil {
		w.WriteHeader(400)
		log.Println(err)
		return
	}

	log.Println("decoding posted image...")
	imgconf, imgfmt, err := image.DecodeConfig(bytes.NewReader(data))

	if err != nil {
		w.WriteHeader(500)
		log.Println(err)
		return
	}

	if imgconf.Width > MAX_WIDTH || imgconf.Height > MAX_HEIGHT {
		w.WriteHeader(400)
		log.Println("rejected image due to dimensions")
		return
	}

	id := uuid.New()

	path := fmt.Sprintf("image-%s-%dx%d-%s.jpg", imgfmt, imgconf.Width, imgconf.Height, id)

	log.Println("uploading " + path + " ... ")

	err = Bucket.Put(path, data, "image/jpeg", s3.PublicRead)

	if err != nil {
		w.WriteHeader(500)
		log.Println(err)
		return
	}

	urlstr := Bucket.URL(path)

	if strings.HasPrefix(urlstr, "https") {
		urlstr = strings.Replace(urlstr, "https", "http", 1)
	}

	w.Header().Set("Location", urlstr)
	w.Header().Set("X-Location", urlstr)

	w.WriteHeader(200)
}

func handlePostImageFile(w http.ResponseWriter, req *http.Request) {

	optionsHandler(w, req)

	file, _, err := req.FormFile("image")

	if err != nil {
		w.WriteHeader(400)
		log.Println(err)
		return
	}

	handleImageHelper(w, req, file)
}

func handlePostImageStream (w http.ResponseWriter, req *http.Request) {

	optionsHandler(w, req)

	handleImageHelper(w, req, req.Body)
}

func optionsHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "GET")
	w.Header().Add("Access-Control-Allow-Methods", "PUT")
	w.Header().Add("Access-Control-Allow-Methods", "POST")
	w.Header().Add("Access-Control-Allow-Methods", "HEAD")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
	w.Header().Add("Access-Control-Allow-Headers", "Location")
	w.Header().Add("Access-Control-Allow-Headers", "X-Location")
	w.Header().Add("Access-Control-Expose-Headers", "Content-Type")
	w.Header().Add("Access-Control-Expose-Headers", "Location")
	w.Header().Add("Access-Control-Expose-Headers", "X-Location")

}

func main () {

	auth, err := aws.EnvAuth()
	
	if err != nil {
		panic(err)
	}

	log.Println(auth)

	s := s3.New(auth, aws.USEast)

	Bucket = s.Bucket(BUCKETID)

	r := mux.NewRouter()
	post := r.Methods("POST", "PUT").Subrouter()
	form := post.Headers("Content-Type", "multipart/form-data").Subrouter()
	jpg := post.Headers("Content-Type", "image/jpeg").Subrouter()
	png := post.Headers("Content-Type", "image/png").Subrouter()
	gif := post.Headers("Content-Type", "image/gif").Subrouter()
	mpeg := post.Headers("Content-Type", "video/mpeg").Subrouter()
	mp3 := post.Headers("Content-Type", "audio/mpeg3").Subrouter()

	r.HandleFunc("/", optionsHandler)
	form.HandleFunc("/image", handlePostImageFile)
	jpg.HandleFunc("/", handlePostImageStream)
	png.HandleFunc("/", handlePostImageStream)
	gif.HandleFunc("/", handlePostImageStream)
	mpeg.HandleFunc("/", handleStream)

	mp3.HandleFunc("/", handleStream)

	http.Handle("/", r)

	err = http.ListenAndServe(":8100", nil)

	if err != nil {

		panic(err)
	}
}
